// Copyright 2012 Sergio, Raul, Dorival. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#ifndef GEMLAB_FATAL_H
#define GEMLAB_FATAL_H

// Std lib
#include <iostream> // for cout
#include <cstdarg>  // for va_list, va_start, va_end
#include <exception>

// Auxiliary
#include "mystring.h"

namespace GemLab {

// Global variable to tell 'catch' that parallel code is activated
bool GEMLAB_CATCH_PARALLEL = true;

// MPI
#ifdef HAS_MPI
  #include <mpi.h>
  #define GEMLAB_FINALIZE  if (GEMLAB_CATCH_PARALLEL) MPI::COMM_WORLD.Abort(666); return 1;
  #define GEMLAB_MPI_CATCH catch (MPI::Exception e) { printf("%sFatal: MPI Error # %d : %s%s\n",TERM_RED,e.Get_error_code(),e.Get_error_string(),TERM_RST); GEMLAB_FINALIZE }
  #define GEMLAB_MPI_INIT  MPI::Init(argc, argv); \
                            MPI::COMM_WORLD.Set_errhandler(MPI::ERRORS_THROW_EXCEPTIONS);
#else
  #define GEMLAB_FINALIZE  return 1;
  #define GEMLAB_MPI_CATCH
  #define GEMLAB_MPI_INIT
#endif

// Boost::Python
#ifdef USE_BOOST_PYTHON
  #include <boost/python/errors.hpp>
  #include <boost/python/str.hpp>
  namespace BPy = boost::python;
  #define GEMLAB_BPY_CATCH catch (BPy::error_already_set) { printf("%sFatal: ",TERM_RED); PyErr_Print(); printf("%s\n",TERM_RST); GEMLAB_FINALIZE }
#else
  #define GEMLAB_BPY_CATCH
#endif

// Catch structure
#define GEMLAB_CATCH catch (GemLab::Fatal * e)   { e->Cout();  delete e;                                                   GEMLAB_FINALIZE } \
                      catch (char const * m)     { printf("%sFatal: %s%s\n",TERM_RED,m       ,TERM_RST);                   GEMLAB_FINALIZE } \
                      catch (std::exception & e) { printf("%sFatal: %s%s\n",TERM_RED,e.what(),TERM_RST);                   GEMLAB_FINALIZE } \
                      GEMLAB_MPI_CATCH                                                                                                       \
                      GEMLAB_BPY_CATCH                                                                                                       \
                      catch (...)                { printf("%sFatal: Some exception (...) occurred%s\n",TERM_RED,TERM_RST); GEMLAB_FINALIZE }

class Fatal
{
public:
	// Constructor
	Fatal (String const & Fmt, ...);

	// Methods
	void   Cout () const { printf("%sFatal: %s%s\n", TERM_RED, _msg.CStr(), TERM_RST); }
	String Msg  () const { return _msg; }

private:
	String _msg;
};


/////////////////////////////////////////////////////////////////////////////////////////// Implementation /////


inline Fatal::Fatal(String const & Fmt, ...)
{
	va_list       arg_list;
	va_start     (arg_list, Fmt);
	_msg.PrintfV (Fmt, arg_list);
	va_end       (arg_list);
}


#ifdef USE_BOOST_PYTHON

void PyExceptTranslator (GemLab::Fatal * e)
{
	PyErr_SetString(PyExc_UserWarning, e->Msg().CStr());
	delete e;
}

#endif // USE_BOOST_PYTHON

}; // namespace GemLab

#endif // GEMLAB_FATAL_H
